package ssd8.socket.http;

import java.io.*;
import java.net.Socket;

/**
 * ServerHandler
 *
 * @author 龙虹宇
 * @version 1.0
 * @date 2023/10/16
 */
public class ServerHandler implements Runnable{
    private static Socket socket;
    private static String rootDirectory;
    // 最大缓冲区大小
    private static int buffer_size = 8192;
    public ServerHandler(Socket socket, String rootDirectory){
        this.socket = socket;
        this.rootDirectory = rootDirectory;
    }
    @Override
    public void run() {
        try {
            handleRequest(socket,rootDirectory);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 处理客户端请求
     *
     * @param clientSocket
     * @param rootDirectory
     * @throws IOException
     */
    private static void handleRequest(Socket clientSocket, String rootDirectory) throws IOException {
        // 获取输入和输出流
        BufferedInputStream bis = new BufferedInputStream(clientSocket.getInputStream());
        BufferedOutputStream out = new BufferedOutputStream(clientSocket.getOutputStream());

        // 读取HTTP请求
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        String request = null;
        try {
            request = in.readLine();
            if (request == null) {
                return;
            }
        } catch (IOException e) {
            return;
        }

        // 判断request合法性
        String[] reqInfo = request.split(" ");
        if ((reqInfo.length != 3) ||
                (!reqInfo[1].startsWith("/")) ||
                (!reqInfo[2].equals("HTTP/1.0") && !reqInfo[2].equals("HTTP/1.1"))) {
            sendResponse(out, "HTTP/1.1 400 Bad Request", "text/html", "Bad Request", 0);
            return;
        }

        // 解析HTTP请求
        boolean isGet = "GET".equals(reqInfo[0]);
        boolean isPut = "PUT".equals(reqInfo[0]);

        String filePath = reqInfo[1];
        // 判断请求类型
        if (isGet) {
            handleGetRequest(filePath, out, rootDirectory);
        } else if (isPut) {
            // 获取文件大小
            String contentLengthInfo;
            do {
                contentLengthInfo = in.readLine();
            }
            while (!contentLengthInfo.startsWith("Content-Length"));
            long fileSize = Long.parseLong((contentLengthInfo.substring(contentLengthInfo.indexOf(":") + 2)));
            handlePutRequest(filePath, fileSize, bis, out, rootDirectory);
        } else {
            // 处理其他HTTP请求，返回错误响应
            sendResponse(out, "HTTP/1.1 501 Not Implemented", "text/html", "Not Implemented", 0);
        }

        // 关闭套接字和流
        in.close();
        out.close();
        clientSocket.close();
    }

    /**
     * 处理 GET请求
     *
     * @param filePath
     * @param out
     * @param rootDirectory
     * @throws IOException
     */
    private static void handleGetRequest(String filePath, OutputStream out, String rootDirectory) throws IOException {
        // filePath为/时，访问index.html
        File file = new File(rootDirectory + filePath + (!("/".equals(filePath)) ? "" : "index.html"));
        System.out.println(rootDirectory +filePath);
        // 判断file合法性
        if (file.exists() && file.isFile()) {
            String contentType = getContentType(file.getName());
            // 发送HTTP响应头
            sendResponse(out, "HTTP/1.1 200 OK", contentType, "", file.length());
            // 发送文件内容
            FileInputStream fis = new FileInputStream(file);
            byte[] buffer = new byte[buffer_size];
            int bytesReadLen;
            while ((bytesReadLen = fis.read(buffer)) != -1) {
                out.write(buffer, 0, bytesReadLen);
                out.flush();
            }
            // 释放资源
            fis.close();
        } else {
            // 文件不存在，返回404响应
            sendResponse(out, "HTTP/1.1 404 Not Found", "text/html", "File Not Found", 0);
        }
    }

    /**
     * 处理 PUT请求
     *
     * @param filePath
     * @param fileSize
     * @param bis
     * @param out
     * @param rootDirectory
     * @throws IOException
     */
    private static void handlePutRequest(String filePath, long fileSize, BufferedInputStream bis, OutputStream out, String rootDirectory) throws IOException {
        File file = new File(rootDirectory + filePath);

        if (file.exists() && file.isFile()) {
            // 文件已存在，返回冲突响应
            sendResponse(out, "HTTP/1.1 409 Conflict", getContentType(filePath), "File Already Exists", 0);
        } else {
            // 创建文件并写入数据
            FileOutputStream fos = new FileOutputStream(file);
            // 接受次数
            long count = fileSize / buffer_size + (fileSize % buffer_size == 0 ? 0 : 1);
            // 接收文件
            int readLen;
            byte[] bytes = new byte[buffer_size];
            while (count-- > 0) {
                readLen = bis.read(bytes);
                fos.write(bytes, 0, readLen);
                fos.flush();
                if (readLen < buffer_size) break;
            }
            if (count != 0) System.out.println("文件已损坏");
            // 释放资源
            fos.close();

            // 成功响应
            sendResponse(out, "HTTP/1.1 201 Created", getContentType(filePath), "File Created", 0);
        }
    }

    /**
     * 定义响应信息
     *
     * @param out
     * @param status
     * @param contentType
     * @param content
     * @param len
     * @throws IOException
     */
    private static void sendResponse(OutputStream out, String status, String contentType, String content, long len) {
        PrintWriter writer = new PrintWriter(out);
        writer.print(status + "\r\n");
        writer.print("Server: MyHttpServer/1.0" + "\r\n");
        writer.print("Content-Type: " + contentType + "\r\n");
        writer.print("Content-Length: " + ("".equals(content) ? len : content.length()) + "\r\n");
        writer.print("\r\n");
        writer.print(content);
        writer.flush();
    }

    /**
     * 获取请求对象的内容
     *
     * @param fileName
     * @return
     */
    private static String getContentType(String fileName) {
        if (fileName.endsWith(".html") || fileName.endsWith(".htm")) {
            return "text/html";
        } else if (fileName.endsWith(".jpg")) {
            return "image/jpeg";
        } else {
            return "application/octet-stream";
        }
    }
}


